"use strict";
/**
 * 描述一个二维的空间线段
 * 直线方程为 y=a+bx
 * 在本模型中,y为因变量,x为自变量
 */
class Line2d {
    constructor(a, b) {
        this.m = null;
        this.n = null;
        this.a = a;
        this.b = b;
        this.startPoint = null;
        this.endPoint = null;
        /** 两个端点之间的距离 */
        this.distance = -1;
        this.startMile = 0;
        this.endMile = 0;
    }
    
    /** 获取距离 */
    getDistance() {
        return Math.sqrt((this.startPoint.e - this.endPoint.e) * (this.startPoint.e - this.endPoint.e) + (this.endPoint.n - this.startPoint.n) * (this.endPoint.n - this.startPoint.n));
    }
    
    /** 
     * 已知起始点和终止点,计算xyz上的分量
     */
    _caculateMNP() {
        var pt1 = this.startPoint;
        var pt2 = this.endPoint;
        this.distance = this.getDistance();
        this.m = (pt2.e - pt1.e) / this.distance;
        this.n = (pt2.n - pt1.n) / this.distance;
        this.p = (pt2.h - pt1.h) / this.distance;  /** 根据平面距离来内插高程 */
    }
    
    /**
     * 根据两点,构造一条直线
     * @param {object} pt1 起始点 {e,n,h,mileage}
     * @param {object} pt2 终止点 {e,n,h,mileage}
     */
    static createLine2d(pt1, pt2) {
        var line = new Line2d();
        line.startPoint = pt1;
        line.endPoint = pt2;
        line._caculateMNP();
        line.a = pt1.n - (line.n * pt1.e) / line.m;
        line.b = line.n / line.m;
        line.startMile = pt1.mileage;
        line.endMile = pt2.mileage;
        return line;
    }
    /**
    * 2d直线的线性回归函数(说明：结合顶管实际情况，需要增加约束条件：1.点的数量；2.点组中最大点间距)
    * @param {Array} ptList 点数组
    */
    static line2dFit(ptList) {
        var xSum = 0, ySum = 0, xAverage = 0, yAverage = 0;
        var n = ptList.length; //点的长度
        for (var i = 0; i < n; i++) {
            xSum += ptList[i].e;
            ySum += ptList[i].n;
        }
        xAverage = xSum / n; /** 求取x方向坐标平均值 */
        yAverage = ySum / n; /** 求取y方向坐标平均值 */
        var xArray, yArray;
        xArray = ptList.map(function (item) {
            return item.e - xAverage;
        });
        yArray = ptList.map(function (item) {
            return item.n - yAverage;
        });

        var xySum = 0, xSquareSum = 0;
        for (var i = 0; i < n; i++) {
            xSum += xArray[i];
            ySum += yArray[i];
            xySum += xArray[i] * yArray[i];
            xSquareSum += xArray[i] * xArray[i];
        }
        
        // e = y-a-bx
        // ∑e^2 = ∑(y-a-bx)^2 , 回归模型为 min(∑e^2),即e的平方和的最小值
        // 上式分别对a,b求偏导,使之各等于0,再解二元一次方程:
        // 不服不辩,参考网址 http://blog.csdn.net/qll125596718/article/details/8248249    
        var b = (n * xySum - xSum * ySum) / (n * xSquareSum - xSum * xSum);
        var atemp = (xSquareSum * ySum - xSum * xySum) / (n * xSquareSum - xSum * xSum); /** 点数组平移后 拟合的直线 */
        var a = atemp + yAverage - b * xAverage; /** 得到点数组平移之前拟合直线 */
        var line = new Line2d(a, b);
        //计算line更多的信息(考虑到顶管的实际情况，未进行点数组的排序和粗差剔除；若后续发现bug，再作调整)
        var startX = ptList[0].e;
        var startY = line.getDependentVar(startX);
        var endX = ptList[n - 1].e;
        var endY = line.getDependentVar(endX);
        line.distance = Math.sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY));
        line.m = (endX - startX) / line.distance;
        line.n = (endY - startY) / line.distance;
        line.p = (ptList[n - 1].h - ptList[0].h) / line.distance;
        var startPoint = ptList[0];
        startPoint.n = startY;        
        line.startPoint = startPoint;
        var endPoint = ptList[n -1];
        endPoint.n = endY;
        line.endPoint = endPoint;
        line.startMile = line.startPoint.mileage;
        line.endMile = line.endPoint.mileage;
        return line;
    }
    /**
     * 获取自变量 
     */
    getIndependentVar(y) {
        return (y - this.a) / this.b;
    }
    
    /** 获取因变量 */
    getDependentVar(x) {
        return this.a + this.b * x;
    }
    /** 
     * 根据距离获取坐标
     */
    getCoorAtMile(mileageDiff) {
        mileageDiff = this.distance * mileageDiff / (this.endMile - this.startMile);
        var x = this.startPoint.e + mileageDiff * this.m;
        var y = this.startPoint.n + mileageDiff * this.n;
        var z = this.startPoint.h + mileageDiff * this.p;
        return { x: x, y: y, z: z, e: x, n: y, h: z };
    }
}
exports.Line2d = Line2d;

// class Line3d {
//     /**
//      * 2点构造直线方程
//      * @param {Array} startXYZ 起始点的xyz坐标数组
//      * @param {Array} endXYZ 终止点的xyz坐标数组
//      */
//     constructor(startXYZ, endXYZ) {
//         /** 该段直线的里程长度 */
//         this.mileageLength = 0;
//         this.startXYZ = startXYZ;
//         this.endXYZ = endXYZ;
//         // 则这条直线可以描述为经过[x0,y0,z0],[x1,y1,z1]的直线,2点式方程可以写为:
//         // x - x0 / x1 - x0= y - y0 / y1 - y0 =z - z0 / z1 - z0,计算后可写为参数式方程
//         // (x - x0) / m = (y - y0) / n =(z - z0) / p
//         this.m = endXYZ[0] - startXYZ[0];
//         this.n = endXYZ[1] - startXYZ[1];
//         this.p = endXYZ[2] - startXYZ[2];
//         this._normalizeMNP(); //归一化mnp
//     }
//     /**
//      * 3d直线求线性回归
//      * @param {Array} xyzArray 包含xyz数组的数组,如[[2,3,5],[3,6,7]]
//      */
//     static line3dFit(xyzArray) {
//         var xArray = xyzArray.map(item => item[0]);
//         var yArray = xyzArray.map(item => item[1]);
//         var zArray = xyzArray.map(item => item[2]);
//         //3d直线拟合的思路如下:
//         //先在XOY平面上,拟合直线,该直线 y = a + bx 在三维坐标系下是一个垂直于XOY平面的面
//         //再到YOZ平面上,拟合直线,该直线 z = c + dy 在三维坐标系下是一个垂直于YOZ平面的面
//         //这两个面在空间上相交的直线就为该3d直线
//         var plateXY = Line2d.line2dFit(xArray, yArray);
//         var plateYZ = Line2d.line2dFit(yArray, zArray);
//         //以xArray两端的数据,在直线上寻找对应的2点
//         var x0 = xArray[0];
//         var y0 = plateXY.getDependentVar(x0);
//         var z0 = plateYZ.getDependentVar(y0);
//         var x1 = xArray[xArray.length - 1];
//         var y1 = plateXY.getDependentVar(x1);
//         var z1 = plateYZ.getDependentVar(y1);
//         var startXYZ = [x0, y0, z0];
//         var endXYZ = [x1, y1, z1];
//         var line3d = new Line3d(startXYZ, endXYZ);
//         return line3d;
//     }
//     /**
//      * 向量归一化,应使 m * m + n * n + p * p=1
//      */
//     _normalizeMNP() {
//         var squareSum = this.m * this.m + this.n * this.n + this.p * this.p;
//         var root = Math.sqrt(squareSum);
//         //本段直线的里程长度
//         this.mileageLength = root;
//         this.m = this.m / root;
//         this.n = this.n / root;
//         this.p = this.p / root;
//     }
//     /**
//      * 通过里程上的长度,求该点的xyz
//      * @return 返回三维坐标数组 [x,y,z]
//      */
//     getXYZ(mileageDiff) {
//         var x = this.startXYZ[0] + mileageDiff * this.m;
//         var y = this.startXYZ[1] + mileageDiff * this.n;
//         var z = this.startXYZ[2] + mileageDiff * this.p;
//         return [x, y, z];
//     }
//     /** 已知x获取y */
//     getY(x) {
//         return this.n * (x - this.startXYZ[0]) / this.m + this.startXYZ[1];
//     }
//     /** 已知x获取z */
//     getZ(x) {
//         return this.p * (x - this.startXYZ[0]) / this.m + this.startXYZ[2];
//     }
// }
// exports.Line3d = Line3d;